package goutil

import (
	"io"
	"runtime"
	"strconv"
	"strings"

	"github.com/rs/zerolog"
)

type GoutilLogLevel int8

const (
	// 日志等级定义
	DebugLevel GoutilLogLevel = iota
	InfoLevel
	WarnLevel
	ErrorLevel
	FatalLevel

	Disabled

	LogTimeFormat = "060102_150405.000000000"
)

// 日志信息结构体
type GoutilLog struct {
	log zerolog.Logger
}

// 全局初始化
func init() {
	zerolog.TimeFieldFormat = LogTimeFormat
}

// 设置全局日志等级
func LogSetLevel(lvl GoutilLogLevel) {
	switch lvl {
	case DebugLevel:
		zerolog.SetGlobalLevel(zerolog.DebugLevel)
	case InfoLevel:
		zerolog.SetGlobalLevel(zerolog.InfoLevel)
	case WarnLevel:
		zerolog.SetGlobalLevel(zerolog.WarnLevel)
	case ErrorLevel:
		zerolog.SetGlobalLevel(zerolog.ErrorLevel)
	case FatalLevel:
		zerolog.SetGlobalLevel(zerolog.FatalLevel)
	case Disabled:
		zerolog.SetGlobalLevel(zerolog.Disabled)
	}
}

// builder设置高亮
func colorizeBuilder(b *strings.Builder, str, color string, noColor bool) {
	if !noColor {
		b.WriteString("\x1B[")
		b.WriteString(color)
		b.WriteString("m")
	}
	b.WriteString(str)
	if !noColor {
		b.WriteString("\x1B[0m")
	}
}

// 字符串设置高亮
func colorizeString(str, color string, noColor bool) string {
	var b strings.Builder
	colorizeBuilder(&b, str, color, noColor)
	return b.String()
}

// 终端日志格式化输出. inefficient!!!
// TODO: writer日志等级控制
// TODO: 子模块日志等级控制
// TODO: Faster pretty logging
func ConsoleFormater(out io.Writer, noColor bool) io.Writer {
	io := zerolog.ConsoleWriter{
		Out:        out,
		NoColor:    noColor,
		TimeFormat: LogTimeFormat,
	}
	io.FormatLevel = func(i interface{}) string {
		ll, ok := i.(string)
		if !ok {
			if i == nil {
				return "???"
			} else {
				return ll[:3]
			}
		}
		switch ll {
		case "trace":
			fallthrough
		case "debug":
			return "DBG"
		case "info":
			return colorizeString("INF", "1;32", io.NoColor)
		case "warn":
			return colorizeString("WRN", "1;33", io.NoColor)
		case "error":
			return colorizeString("ERR", "1;31", io.NoColor)
		case "fatal":
			fallthrough
		case "panic":
			return colorizeString("PNC", "1;35", io.NoColor)
		default:
			return "???"
		}
	}
	io.FormatCaller = func(i interface{}) string {
		pc, file, line, ok := runtime.Caller(zerolog.CallerSkipFrameCount + 7)
		if !ok {
			return " "
		}
		var b strings.Builder
		colorizeBuilder(&b, runtime.FuncForPC(pc).Name(), "3", io.NoColor)
		b.WriteString(" ")
		colorizeBuilder(&b, file[strings.LastIndexByte(file, '/')+1:], "1",
			io.NoColor)
		b.WriteString(":")
		colorizeBuilder(&b, strconv.Itoa(line), "1;36", io.NoColor)
		b.WriteString(" ")
		return b.String()
	}
	return io
}

// 使用writer初始化日志
func NewLogWithWriters(writers ...io.Writer) *GoutilLog {
	l := zerolog.New(zerolog.MultiLevelWriter(writers...)).
		With().Timestamp().Logger().
		With().Caller().Logger()
	return &GoutilLog{log: l}
}

// 不定参数解析
// 如果第一个参数不是字符串类型, 则添加一个格式化字符串
func parseParams(v ...interface{}) (f string, r []interface{}) {
	if len(v) == 0 {
		return "", nil
	}
	if _, ok := v[0].(string); ok {
		f = v[0].(string)
		r = v[1:]
		return
	}
	var b strings.Builder
	for i := 0; i < len(v); i++ {
		b.WriteString("%v")
	}
	f = b.String()
	r = v
	return
}

func (s GoutilLog) Debug(v ...interface{}) {
	fmt, r := parseParams(v...)
	s.log.Debug().Msgf(fmt, r...)
}
func (s GoutilLog) Info(v ...interface{}) {
	fmt, r := parseParams(v...)
	s.log.Info().Msgf(fmt, r...)
}
func (s GoutilLog) Warn(v ...interface{}) {
	fmt, r := parseParams(v...)
	s.log.Warn().Msgf(fmt, r...)
}
func (s GoutilLog) Error(v ...interface{}) {
	fmt, r := parseParams(v...)
	s.log.Error().Msgf(fmt, r...)
}
func (s GoutilLog) Fatal(v ...interface{}) {
	fmt, r := parseParams(v...)
	s.log.Fatal().Msgf(fmt, r...)
}
